/**********************************************************************
 ** Copyright (C) 2009 Nokia Corporation.
 ** All rights reserved.
 **
 ** $LICENSE_TEXT$
 **********************************************************************/

#include "btserialportclient.h"
#include "serialCommHandler/qserialport.h"

#include <QDBusInterface>
#include <QDBusObjectPath>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
#include <QVariant>
#include "qdatetime.h"
#include <QDebug>

BtSerialPortClient::BtSerialPortClient(QObject *parent) :
    QObject(parent)
{
    m_isConnected = false;
}

BtSerialPortClient::~BtSerialPortClient()
{
}

void BtSerialPortClient::setupInterface(QString address)
{
    QDBusConnection systemBus = QDBusConnection::systemBus();
    QDBusInterface managerInterface("org.bluez", "/", "org.bluez.Manager", systemBus, this);

    QDBusMessage callReturn = managerInterface.call("DefaultAdapter");
    QString objectPath = firstArgumentToObjectPath(callReturn);
    if (isError(objectPath.isNull()))
        return;

    QDBusInterface adapterInterface("org.bluez", objectPath, "org.bluez.Adapter", systemBus, this);
    qDebug() << "Adapter Interface" << adapterInterface.isValid();
    if (isError(!adapterInterface.isValid()))
        return;

    QDBusMessage devicePathMessage = adapterInterface.call("FindDevice", address);
    qDebug() << devicePathMessage;
    if (devicePathMessage.type() == QDBusMessage::ErrorMessage) {
        devicePathMessage = adapterInterface.call("CreateDevice", address);
        qDebug() << devicePathMessage;
    }

    if (isErrorMessage(devicePathMessage))
        return;

    m_devicePath = firstArgumentToObjectPath(devicePathMessage);
    if (isError(m_devicePath.isNull()))
        return;

    QDBusInterface serialInterface("org.bluez", m_devicePath, "org.bluez.Serial", systemBus, this);
    if (isError(!serialInterface.isValid()))
        return;

    QDBusPendingCall async = serialInterface.asyncCall("Connect", "spp");
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this);

    QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
        this, SLOT(connectionCallFinishedSlot(QDBusPendingCallWatcher*)));

}

void BtSerialPortClient::updateConnectionStatus(const QDBusMessage& message)
{
    QList<QVariant> result = message.arguments();
    QString property = result.at(0).toString();
    bool isConnected = result.at(1).value<bool> ();

    qDebug() << "property: " << property;
    qDebug() << "isConnected: " << isConnected;

    if (!property.compare("Connected") && (isConnected == false)) {
        disconnect();
    }
}

void BtSerialPortClient::connectionCallFinishedSlot(QDBusPendingCallWatcher* call)
{
    QDBusMessage serialMessage = call->reply();

    if (!isErrorMessage(serialMessage)) {
        QDBusConnection systemBus = QDBusConnection::systemBus();
        QVariant serial = serialMessage.arguments().takeFirst();
        QString serialString = serial.toString();

        QSerialPort *serialPort = NULL;
        if (!serialString.isEmpty()) {
            serialPort = QSerialPort::create(serialString, 115200, true);

            // Being an observer for connection status from bluez
            QDBusInterface deviceInterface("org.bluez", m_devicePath, "org.bluez.Device",
                    systemBus, this);
            if (!deviceInterface.isValid()) {
                return;
            }
            systemBus.connect("org.bluez", m_devicePath, "org.bluez.Device", "PropertyChanged",
                    this, SLOT(updateConnectionStatus(const QDBusMessage&)));

            m_isConnected = true;
            emit interfaceBounded(serialPort);
        } else {
            emit error();
        }

    }
}

QString BtSerialPortClient::firstArgumentToObjectPath(QDBusMessage message)
{
    QVariant messageVariant = message.arguments().takeFirst();
    QString objectPath;
    if (messageVariant.canConvert<QDBusObjectPath> ()) {
        QDBusObjectPath argument = messageVariant.value<QDBusObjectPath> ();
        objectPath = argument.path();
    }
    return objectPath;
}

bool BtSerialPortClient::isError(bool errorTest)
{
    if (errorTest) {
        emit error();
    }
    return errorTest;
}

bool BtSerialPortClient::isErrorMessage(QDBusMessage message)
{
    bool errorTest = message.type() == QDBusMessage::ErrorMessage;
    if (isError(errorTest))
        qDebug() << "Error message: " << message.errorMessage();
    return errorTest;
}

void BtSerialPortClient::disconnect()
{
    qDebug() << "disconnect called";

    if(m_isConnected) {
        QDBusInterface serialInterface("org.bluez", m_devicePath, "org.bluez.Serial",
                QDBusConnection::systemBus(), this);

        if (isError(!serialInterface.isValid()))
            return;

        QDBusMessage serialMessage = serialInterface.call("Disconnect", "spp");

        if (isErrorMessage(serialMessage)) {
            qDebug() << "error disconnecting serial interface";
        } else {
            qDebug() << "successfull disconnection";
            m_isConnected = false;
            emit disconnected();
        }
    }
}
